(** Functional Reactive core functions *)

(** {2 Useful functions} *)

val id: 'a -> 'a
val dup: 'a -> 'a * 'a
val some: 'a -> 'a option

val curry2: ('a * 'b -> 'c) -> 'a -> 'b -> 'c
val uncurry2: ('a -> 'b -> 'c) -> 'a * 'b -> 'c
val curry3: (('a * 'b) * 'c -> 'd) -> 'a -> 'b -> 'c -> 'd
val uncurry3: ('a -> 'b -> 'c -> 'd) -> ('a * 'b) * 'c -> 'd

val first: 'a -> 'b -> 'a
val second: 'a -> 'b -> 'b

(** Compare two objects structurally with [(=)], falling back to physical
    equality with [(==)] if this is not possible. *)
val ( =* ): 'a -> 'a -> bool

(** The negation of [=*]. *)
val ( <>* ): 'a -> 'a -> bool


(** {2 Data types} *)

(** The type of events. *)
type +'a event
(** An event is a signal which occurs as discrete points in time. *)

(** The type of behaviors. *)
type +'a behavior
(** A behavior is a signal which occurs continuously throughout time. *)


(** {2 Events} *)

(** {3 Basic events} *)

(** [never] is an event which never occurs. *)
val never: 'a event

(** [now] is an event which occurs exactly once, when a it is first
    instantiated (i.e. registered or switched to). *)
val now: unit event
(** TODO: should this accept an argument? (we can currently achieve this
    with tag_e) *)


(** {3 Event receivers} *)

(** The type of event receivers. *)
type -'a event_receiver

(** Create a new event receiver. *)
val event_receiver: unit -> 'a event_receiver * 'a event

(** Send a value to an event receiver. *)
val send_event: 'a event_receiver -> 'a -> unit


(** {3 Event functions} *)

(** [tag_e v a] replaces the value of the event [a] with [v]. *)
val tag_e: 'b -> 'a event -> 'b event

(** [map_e f e] transforms the value of the event [a] with [f]. *)
val map_e: ('a -> 'b) -> 'a event -> 'b event
(** [(on e -> f)] is syntactic sugar for [map_e f e], and
    [(on e a ... -> ...)] is syntactic sugar for [map_e (fun a ... -> ...) e]. *)

(** [filter_e f e] passes all occurrences of [e] for which [f] returns [true]. *)
val filter_e: ('a -> bool) -> 'a event -> 'a event

(** [may_e e] occurs whenever [e] is [Some v], and has the value [v]. *)
val may_e: 'a option event -> 'a event

(** [zip_e a b] occurs whenever both [a] and [b] occur, and has the value of
    [a] paired with [b]. *)
val zip_e: 'a event -> 'b event -> ('a * 'b) event
(** TODO: should this accept a tuple instead of two arguments? *)

(** [unzip_e e] produces two events synchronous with [e] whose values are the
    first and second halves of the value of [e]. *)
val unzip_e: ('a * 'b) event -> 'a event * 'b event

(** [merge_e l] occurs when any event in [l] occurs, with the value of that
    event.  If more than one event occurs at the same time, the leftmost
    event takes precedence. *)
val merge_e: 'a event list -> 'a event
(** TODO: should this also accept a function used to resolve collisions?
    (we can currently acheive this with par_e and List.fold_left) *)

(** [par_e l] occurs when at least one event in [l] occurs, with the value of
    all occuring events joined in a list.  List order is preserved. *)
val par_e: 'a event list -> 'a list event

(** [once_e e] passes only the first occurrence of [e]. *)
val once_e: 'a event -> 'a event

(** [snapshot_e e trigger] occurs whenever event [trigger] occurs.  If event
    [e] occurs simultaneously, its value is used, otherwise the event occurs
    with the value [None]. *)
val snapshot_e: 'a event -> 'b event -> 'a option event


(** {3 Accumulators} *)

(** [accum_e i e] creates a event with memory which occurs whenever its
    internal state changes.  [e] is an event which carries a function which
    is used to update the internal state each time it occurs.  The initial
    value of the state is given by [i]. *)
val accum_e: 'a -> ('a -> 'a) event -> 'a event

(** [collect_e f i e] is similar to [accum_e] but passes the value of the
    event [e] to the function [f] in order to update the internal state. *)
val collect_e: ('a -> 'b -> 'a) -> 'a -> 'b event -> 'a event


(** {3 Converters} *)

(** [changes b] occurs whenever [b] changes, and has the new value of [b].
    Equality is determined with [( =* )]. *)
val changes: 'a behavior -> 'a event

(** [changesq b] is similar to [changes b] but detects changes using
    physical equality. *)
val changesq: 'a behavior -> 'a event

(** [delta f b] is similar to [changesq b] but the value of the event it
    creates is determined by applying [f] to the old and new values,
    respectively, of [b].  For example, [delta first b] emits the value [b]
    had just before it changed. *)
val delta: ('a -> 'a -> 'b) -> 'a behavior -> 'b event

(** [when_e b] occurs whenever [b] becomes [true]. *)
val when_e: bool behavior -> unit event
(** TODO: should this be renamed [when]? *)

(** [edge_e b] occurs whenever [b] transitions from [None] to [Some v], and
    has the value [v]. *)
val edge_e: 'a option behavior -> 'a event
(** TODO: should this be renamed [edge]? *)


(** {3 Higher-order functions } *)

(** [switch_e i e] is an event isomorphic to the last occurrence of [e] (or
    [i], if [e] has not yet occurred). *)
val switch_e: 'a event -> 'a event event -> 'a event

(** [collapse_e b] is an event isomorphic to the current value of [b]. *)
val collapse_e: 'a event behavior -> 'a event

(** [fix_e g f] allows [f] to reference an event before it is defined: the
    result of applying [f] to this "phantom" event is passed to [g], whose
    output is used to "fill in" the phantom event.  [fix_e] returns the
    output of [f]. *)
val fix_e: ('b -> 'a event) -> ('a event -> 'b) -> 'b
(** TODO: this should have some syntactic sugar associated with it *)


(** {2 Behaviors} *)

(** {3 Cells} *)

(** The type of cells. *)
type -'a cell

(** Create a new cell with the given initial value. *)
val new_cell: 'a -> 'a cell * 'a behavior

(** Set the value of a cell. *)
val set_cell: 'a cell -> 'a -> unit


(** {3 Basic functions} *)

(** [snapshot b e] is an event which occurs with the value of [b] when
    the event [e] occurs. *)
val snapshot_b: 'a behavior -> 'b event -> 'a event

(** [latch b e] is similar to [snapshot b e] but carries the value [b]
    had just before [e] occured. *)
val latch: 'a behavior -> 'b event -> 'a event

(** [hold i e] is a behavior equal to the last occurrence of [e] (or [i], if
    [e] has not yet occurred). *)
val hold: 'a -> 'a event -> 'a behavior

(** [gate b e] is a behavior which follows [b], but changes only when
    the event [e] occurs. *)
val gate_b: 'a behavior -> 'b event -> 'a behavior

(** [track i b] is a behavior which follows [b] whenever it is not [None]. *)
val track: 'a -> 'a option behavior -> 'a behavior

(** [zip_b a b] is a behavior with the value of [a] paired with [b]. *)
val zip_b: 'a behavior -> 'b behavior -> ('a * 'b) behavior

(** [unzip_b b] produces two behaviors whose values are the first and
    second halves of the value of [b]. *)
val unzip_b: ('a * 'b) behavior -> 'a behavior * 'b behavior

(** [par_b l] is a behavior whose value is a list of the values of all
    behaviors in the list [l].  List order is preserved. *)
val par_b: 'a behavior list -> 'a list behavior

(** [accum_b] is identical to [accum_e] but produces a behavior rather than
    an event. *)
val accum_b: 'a -> ('a -> 'a) event -> 'a behavior

(** [collect_b] is identical to [collect_e] but produces a behavior rather
    than an event. *)
val collect_b: ('a -> 'b -> 'a) -> 'a -> 'b event -> 'a behavior


(** {3 Higher-order functions} *)

(** [switch_b i e] is a behavior isomorphic to the last
    occurrence of [e] (or [i], if [e] has not yet occurred). *)
val switch_b: 'a behavior -> 'a behavior event -> 'a behavior

(** [sswitch_b g f i e] is similar to [switch_b] but allows state to be
    maintained across switches.  [g] is a function which extracts the
    current state of a behavior from its value.  [e] is an event which
    carries a function which produces a behavior when given an initial
    state.  [f] applied to [i] determines the initial behavior. *)
val sswitch_b: ('a -> 'b) -> ('b -> 'a behavior) -> 'b ->
	('b -> 'a behavior) event -> 'a behavior
(** Note that the state of [g] must be fully observable from its output
    value for [sswitch_b] to work as expected. *)

(** [collapse_b b] is a behavior isomorphic to the current value of [b]. *)
val collapse_b: 'a behavior behavior -> 'a behavior

(** [scollapse_b g i e] is similar to [collapse_b] but allows state to be
    maintained across switches.  [g] is a function which extracts the
    current state of a behavior from its value.  [b] is an event which
    carries a function which produces a behavior when given an initial
    state.  The initial value of [b] applied to [i] determines the initial
    behavior. *)
val scollapse_b: ('a -> 'b) -> 'b -> ('b -> 'a behavior) behavior -> 'a behavior
(** Note that the state of [g] must be fully observable from its output
    value for [scollapse_b] to work as expected. *)

(** [fix_b] is identical to [fix_e] but works with a behavior. *)
val fix_b: ('b -> 'a behavior) -> ('a behavior -> 'b) -> 'b


(** {3 Lifting} *)

(** [lift0 a] is a behavior which is always equal to [a]. *)
val lift0: 'a -> 'a behavior

(** [lift1 f] returns a function which, when applied to a behavior,
    returns a behavior which is always equal to [f] applied
    to the current value of the behavior. *)
val lift1: ('a -> 'b) -> ('a behavior -> 'b behavior)
val lift2: ('a -> 'b -> 'c) -> ('a behavior -> 'b behavior -> 'c behavior)
val lift3: ('a -> 'b -> 'c -> 'd) -> ('a behavior -> 'b behavior -> 'c behavior -> 'd behavior)
val lift1q: ('a -> 'b) -> ('a behavior -> 'b behavior)
val lift2q: ('a -> 'b -> 'c) -> ('a behavior -> 'b behavior -> 'c behavior)
val lift3q: ('a -> 'b -> 'c -> 'd) -> ('a behavior -> 'b behavior -> 'c behavior -> 'd behavior)


(** {2 Endpoints} *)

(** The type of a endpoint. *)
type endpoint

(** [register_e e] marks the event [e] as an endpoint. *)
val register_e: 'a event -> endpoint
(** [register_b b] marks the behavior [b] as an endpoint. *)
val register_b: 'a behavior -> endpoint
(** [unregister h] destroys the endpoint [h].  It is guaranteed not to be evaluated again. *)
val unregister: endpoint -> unit


(** {2 Lazy signal sources} *)

val event_source: ('a event_receiver -> 'b) -> ('b -> unit) -> 'a event
val behavior_source: (unit -> 'a) -> ('a cell -> 'b) -> ('b -> unit) -> 'a behavior


(** {2 Mixed-mode signals (experimental)} *)

module Mixed: sig
	type +'a t

	val construct: 'a behavior -> 'a event -> 'a t
	val of_event: 'a event -> 'a option t
	val of_behavior: 'a behavior -> 'a t
	val with_event: 'a t -> 'a event -> 'a t
	val with_behavior: 'a t -> 'a behavior -> 'a t
	val behavior_part: 'a t -> 'a behavior
	val event_part: 'a t -> 'a event
	val destruct: 'a t -> 'a behavior * 'a event

	val nothing: 'a option t
	val map: ('a -> 'b) -> 'a t -> 'b t
	val merge: ('a -> 'b -> 'c) -> 'a t -> 'b t -> 'c t
	val zip: 'a t -> 'b t -> ('a * 'b) t
	val snapshot: 'a t -> 'b event -> 'a event
	val unzip: ('a * 'b) t -> 'a t * 'b t
	val par: 'a t list -> 'a list t
	val may: 'a -> 'a option t -> 'a t

	val switch: 'a t -> 'a t event -> 'a t
	val collapse: 'a t behavior -> 'a t
	val meta: 'a t t -> 'a t
	val fix: ('b -> 'a t) -> ('a t -> 'b) -> 'b
end

type +'a mixed = 'a Mixed.t
